你是否曾經面對過需要遍歷一個集合、而又不希望暴露它的內部結構的情況?這就像我們在看一本書時,並不需要知道書本是如何裝訂的,只需要翻頁就好。這時迭代器模式就派上用場了。迭代器模式提供了一種簡單且一致的方式來訪問集合中的元素,讓你不必為了遍歷資料而大費周章。
迭代器模式是一種行為設計模式,它允許你逐一訪問集合物件的元素,而不需要暴露其底層的表示方式。想像你有一個儲存很多物件的容器,但你並不想揭露這些物件如何被儲存的細節。透過迭代器模式,你可以透過一個統一的界面來訪問這些元素,無論是陣列、鏈結串列還是其他資料結構,都可以輕鬆遍歷。
假設我們有一個音樂播放清單,這個清單內部可能是用陣列或鏈結串列來儲存歌曲,但作為使用者,你只想能夠播放下一首歌,不關心它的儲存結構。我們可以使用迭代器模式來實現這個功能。
首先我們需要定義一個迭代器介面,這個介面會告訴使用者是否有下一首歌,並且允許他們拿到下一首歌曲,
// 迭代器介面
class Iterator {
public:
virtual bool hasNext() = 0;
virtual std::string next() = 0;
virtual ~Iterator() {}
};
接下來我們建立一個音樂清單類別,並且實作一個迭代器,這個迭代器會知道如何遍歷歌曲清單,
// 音樂清單迭代器
class PlaylistIterator : public Iterator {
private:
std::vector<std::string> songs;
int position;
public:
PlaylistIterator(const std::vector<std::string>& songs)
: songs(songs), position(0) {}
bool hasNext() override {
return position < songs.size();
}
std::string next() override {
if (hasNext()) {
return songs[position++];
}
return "";
}
};
// 音樂清單類別
class Playlist {
private:
std::vector<std::string> songs;
public:
void addSong(const std::string& song) {
songs.push_back(song);
}
Iterator* createIterator() {
return new PlaylistIterator(songs);
}
};
在客戶端使用時,使用者不需要知道清單如何儲存歌曲,他們只需要使用迭代器提供的 next()
方法來依序播放,
int main() {
Playlist playlist;
playlist.addSong("Song 1");
playlist.addSong("Song 2");
playlist.addSong("Song 3");
Iterator* it = playlist.createIterator();
while (it->hasNext()) {
std::cout << "Playing: " << it->next() << std::endl;
}
delete it;
return 0;
}
在這個例子中,我們定義了一個 Iterator
介面,並且在 PlaylistIterator
中實作了這個介面,用來遍歷歌曲清單。使用者只需要呼叫 hasNext()
和 next()
方法來逐步訪問清單中的歌曲,完全不用擔心清單的內部實現。
迭代器模式的優點在於它提供了一種統一的方式來遍歷集合,不論集合的內部結構如何變化。就像你可以用同樣的方法翻閱紙質書或電子書一樣,迭代器讓不同類型的集合能夠提供相同的操作方式。這使得程式碼變得更加靈活,因為你不需要修改訪問邏輯來適應不同的集合類型。
迭代器模式也有一些缺點。由於每一種集合類型都需要自己的迭代器實現,這會增加一些額外的程式碼。並且在某些情況下,如果集合的大小或複雜度較大,迭代器的操作效能可能會受到影響。除此之外,記憶體管理也需要謹慎處理。
迭代器模式在需要遍歷複雜集合結構時非常實用,它提供了一個簡單、統一的操作介面。透過將遍歷行為封裝在迭代器中,程式設計師能夠專注於如何使用集合,而不是關心其內部細節。正如我們看書時只需專注於閱讀,不必擔心書本的裝訂一樣,迭代器模式讓操作集合變得更加直觀。
更多C++語言相關的文章,歡迎追蹤我的部落格。
https://shengyu7697.github.io/cpp-iterator-pattern/